#include "glwidget.h"

#if defined(Q_OS_MAC)
#include <OpenGL.h>
#endif

GLWidget::GLWidget(MyCallback *myCallback_, QWidget *parent) :
    QGLWidget(parent), timer(new QBasicTimer), myCallback(myCallback_)
{
    painter = new QPainter(this);
    setAutoBufferSwap(false);

    values = 128;
    currentPosition = 0;
    valuesSpectro = Sbs2Common::samplingRate()/2;

    pathGenerated = 0;
    pathSpectroGenerated = 0;

    gyroX = gyroY = 0;

    startY = -1;
    scale = 20;
    lastD = 0;
    startX = -1;
    currentX = 0;

    timeSeries = 1;
    filterSetting = 0;
    scalpmapVisible = 0;

    paused = 0;

    filterText = "filter off";
    timeSeriesText = "time series";
    playText = "playing";
    scalpmapText = "values";

    normalizationOn = 1;
}

void GLWidget::gyroSlot(int gyroX_, int gyroY_)
{
    gyroX = gyroX_;
    gyroY = gyroY_;

    gyroPosX += (1660-gyroX)/10.0;
    if (gyroPosX > this->w) gyroPosX = this->w;
    if (gyroPosX < 0) gyroPosX = 0;
    if (gyroPosX < this->w/2) gyroPosX += this->w/500.0;
    if (gyroPosX > this->w/2) gyroPosX -= this->w/500.0;

    gyroPosY -= (1660-gyroY)/10.0;
    if (gyroPosY > this->h) gyroPosY = this->h;
    if (gyroPosY < 0) gyroPosY = 0;
    if (gyroPosY < this->h/2) gyroPosY += this->h/500.0;
    if (gyroPosY > this->h/2) gyroPosY -= this->h/500.0;

}

void GLWidget::resizeGL(int w, int h)
{
    this->w = w;
    this->h = h;
    gyroPosX = this->w/2.0;
    gyroPosY = this->h/2.0;
    aspectRatio = (qreal) w / (qreal) h;
    generatePaths();
    generateUI();

}

void GLWidget::generatePaths()
{

    if (this->w != 1280)
        return;
    if (pathGenerated)
        return;

    pathGenerated = 1;

    valueToValueDistance = floor(this->w/this->values);


    for (int channel = 0; channel < 2; ++channel)
    {
        channels.append(new QPainterPath());
        rawValues.append(QList<int>());
        channels.last()->moveTo(0,8*50);
        for (int i = 0; i < values-1; ++i)
        {
            channels.last()->lineTo(channels.last()->currentPosition().x() + this->valueToValueDistance,channels.last()->currentPosition().y());
            rawValues[channel].append(0);

        }
        rawValues[channel].append(0);

    }

    generateGrid();
}


void GLWidget::generateGrid()
{

    for (int i = 0; i < values/32 - 1; ++i)
    {
        gridLines.append(new QLine(channels.at(0)->elementAt((i+1)*32).x,0,channels.at(0)->elementAt((i+1)*32).x,this->h));
    }

}



void GLWidget::generateUI()
{
    quitRect = QRect(this->w - toggleFilterRect.width() + 60, toggleFilterRect.y(), toggleFilterRect.width() - 60, toggleFilterRect.height());
}

void GLWidget::update(int index)
{

    if (paused)
        return;

    QVector<QList<int> > vs = myCallback->values[index];

    currentPosition = (currentPosition+vs.at(0).size())%values;
    for (int channel = 0; channel<vs.size(); ++channel)
    {


        int mean = 0;
	int max = -1;
	int min = 999999;
        for (int s = 0; s < rawValues.at(channel).size(); ++s)
	{
            mean += rawValues.at(channel).at(s);
	    if (rawValues.at(channel).at(s) < min)
		min = rawValues.at(channel).at(s);
	    if (rawValues.at(channel).at(s) > max)
		max = rawValues.at(channel).at(s);
	}
        mean /= rawValues.at(channel).size();


        int u = vs.at(0).size()-1;
        for (int p = (currentPosition - 1); p >= (currentPosition - vs.at(0).size()); --p)
        {
            int pp = p;
            if (pp < 0) pp = this->values + pp;
	    double v = 0;
	    if (!normalizationOn)
		v = (mean - vs.at(channel).at(u))/scale + 8*50;
	    else
		v = ((vs.at(channel).at(u)-min)/(double)(max-min)-0.5)*10*scale + 8*50;

            channels.at(channel)->setElementPositionAt(pp,channels.at(channel)->elementAt(pp).x,v);

            rawValues[channel][pp] = vs.at(channel).at(u);

            --u;
        }
    }

//    qDebug() << "0" <<correlation(&rawValues[0], &rawValues[1]);
//    qDebug() << "1" <<correlation(&rawValues[0].mid(1), &rawValues[1].mid(0,rawValues[1].length()-1));
//    qDebug() << "-1" <<correlation(&rawValues[1].mid(1), &rawValues[0].mid(0,rawValues[0].length()-1));

}


void GLWidget::initializeGL()
{
#if defined(Q_OS_MAC)
    const GLint swapInterval = 1;
    CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &swapInterval);
#endif
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    timer->start(1/50 * 1000.0, this);
}

void GLWidget::paintGL()
{

    painter->begin(this);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);


    painter->setPen(QPen(QBrush(QColor(0,0,0,120)),1));
    foreach (QLine* gridLine, gridLines)
    {
        painter->drawLine(*gridLine);
    }



    painter->setPen(QPen(QBrush(QColor("red")),2));
    painter->drawLine(channels.at(0)->elementAt(currentPosition).x,0,channels.at(0)->elementAt(currentPosition).x,this->h);


    if (!scalpmapVisible)
    {

        painter->fillRect(quitRect, QBrush(QColor("black")));



        painter->setFont(QFont("helvetica",8));
        painter->setPen(QColor("white"));

        painter->drawText(quitRect, Qt::AlignCenter, "quit");

    }


    for (int j = 0; j < channels.size(); ++j)
    {
        if (j == 0)
            painter->setPen(QPen(QBrush(QColor("blue")),2));
        if (j == 1)
            painter->setPen(QPen(QBrush(QColor("green")),2));
        if (j == 2)
            painter->setPen(QPen(QBrush(QColor("red")),2));
        if (j == 3)
            painter->setPen(QPen(QBrush(QColor("black")),2));
        if (j == 4)
            painter->setPen(QPen(QBrush(QColor("purple")),2));
        if (j == 5)
            painter->setPen(QPen(QBrush(QColor("orange")),2));
        if (j == 6)
            painter->setPen(QPen(QBrush(QColor("indigo")),2));
        if (j == 7)
            painter->setPen(QPen(QBrush(QColor("olive")),2));
        if (j == 8)
            painter->setPen(QPen(QBrush(QColor("salmon")),2));
        if (j == 9)
            painter->setPen(QPen(QBrush(QColor("magenta")),2));
        if (j == 10)
            painter->setPen(QPen(QBrush(QColor("lime")),2));
        if (j == 11)
            painter->setPen(QPen(QBrush(QColor("darkred")),2));
        if (j == 12)
            painter->setPen(QPen(QBrush(QColor("navy")),2));
        if (j == 13)
            painter->setPen(QPen(QBrush(QColor("brown")),2));

        painter->drawPath(*(channels.at(j)));


    }



    painter->setFont(QFont("helvetica",8));
    painter->setPen(QColor("black"));






    painter->drawEllipse(gyroPosX-5,gyroPosY-5,10,10);

    if (startY != -1)
    {
        painter->setFont(QFont("helvetica",80));
        painter->drawText(this->w/2 - 80,this->h/2 - 20,QString::number(scale));
    }



    painter->end();
    swapBuffers();

}

void GLWidget::timerEvent(QTimerEvent *)
{
    updateGL();
}

void GLWidget::toggleFilter()
{
    filterSetting = !filterSetting;

    if (filterSetting)
    {
        emit turnFilterOn(8,30,32);
        filterText = "8-30Hz";
    }
    else
    {
        emit turnFilterOff();
        filterText = "filter off";
    }

}

void GLWidget::toggleTimeSeries()
{

    timeSeries = !timeSeries;

    if (timeSeries)
    {
        emit turnSpectrogramOff();
        timeSeriesText = "time series";
    }
    else
    {
        emit turnSpectrogramOn(128,128,32);
        timeSeriesText = "frequency";
    }
}

void GLWidget::togglePlay()
{
    paused = !paused;
    if (paused)
    {
        playText = "paused";
    }
    else
    {
        playText = "playing";
    }
}

void GLWidget::toggleHardware()
{
    if (Sbs2Common::getCurrentHardware() == "emocap")
        myCallback->setHardware("emotiv");
    else
        myCallback->setHardware("emocap");
}



void GLWidget::mousePressEvent(QMouseEvent *event)
{

    if (event->button() != Qt::LeftButton)
        return;

    if (toggleFilterRect.contains(event->pos()))
    {
        toggleFilter();
        return;
    }

    if (toggleTimeSeriesRect.contains(event->pos()))
    {
        toggleTimeSeries();
        return;
    }

    if (togglePlayRect.contains(event->pos()))
    {
        togglePlay();
        return;
    }

    if (toggleHardwareRect.contains(event->pos()))
    {
        toggleHardware();
        return;
    }
    if (toggleScalpmapRect.contains(event->pos()))
    {

        return;
    }

    if (quitRect.contains(event->pos()))
    {
        QApplication::quit();
    }

    startX = event->pos().x();
    currentX = startX;
    startY = event->pos().y();
    lastD = 0;

}


void GLWidget::mouseMoveEvent(QMouseEvent *event)
{

    if (startY == -1)
        return;

    if (startX == -1)
        return;

    int v = (int)(event->pos().y() - startY)/50;

    if (v != lastD)
    {

        scale += v;

        if (scale < 1.0) scale = 1.0;
        if (scale > 100.0) scale = 100.0;

        startY = event->pos().y();

    }

    lastD = v;

    currentX = event->pos().x();

}

void GLWidget::mouseReleaseEvent(QMouseEvent *event)
{
    startY = -1;
    lastD = 0;
    startX = -1;
}

void GLWidget::kill()
{
    system("su -c 'killall -9 org.kde.necessitas.example.EegViewer'");
}

double GLWidget::correlation(QList<int> *v1, QList<int> *v2)
{
    if (v1->size() != v2->size())
	return -1;

    double mean1 = 0;
    double mean2 = 0;
    double sum1 = 0;
    double sum2 = 0;

    for (int i = 0; i<v1->size(); ++i)
    {
	sum1 += v1->at(i);
	sum2 += v2->at(i);
    }

    mean1 = sum1/(double)v1->size();
    mean2 = sum2/(double)v2->size();

    double squaredSum1 = 0;
    double squaredSum2 = 0;
    double dSum = 0;

    for (int i = 0; i<v1->size(); ++i)
    {
	dSum += ((v1->at(i) - mean1)*(v2->at(i) - mean2));
	squaredSum1 += pow((v1->at(i)-mean1),2.0);
	squaredSum2 += pow((v2->at(i)-mean2),2.0);

    }
    double t = 1e-20;
    return (dSum/(double)(sqrt(squaredSum1*squaredSum2))+t);

}


